/******************************************************************************\
    Format.cpp contains classes for data format.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#include ".\Format.h"
#include ".\String.h"
#include ".\StdFile.hpp"
#include <stdio.h>
using namespace std;

inline static bool IsPrinted( char c )
{
	return 33 <= c && c <= 127;
}

inline static bool IsLegal( char c )
{
	return IsPrinted( c ) && c != '=' && c != '{' && c != ';' && c != '}' && c != '[' && c != ',' && c != ']' && c != '"';
}

std::string cFormat::Encode( const std::string &str )
{
	int i;
	string result;
	for( i = 0; i < ( int )str.length(); ++i )
	{
		if( IsLegal( str[ i ] ) && str[ i ] != '%' && str[ i ] != '\\' )
			result += str[ i ];
		else if( IsPrinted( str[ i ] ) )
			result += StringPrintf( "\\%c", str[ i ] );
		else
			result += StringPrintf( "%%%02x", ( int )( unsigned )( unsigned char )str[ i ] );
	}
	return result;
}

std::string cFormat::Decode( const std::string &str )
{
	int i;
	string result;
	for( i = 0; i < ( int )str.length(); ++i )
	{
		if( str[ i ] == '%' && i + 2 < ( int )str.length() && isxdigit( str[ i + 1 ] ) && isxdigit( str[ i + 2 ] ) )
		{
			char ch = 0;
			for( int j = 1; j <= 2; ++j )
			{
				ch <<= 4;
				if( isdigit( str[ i + j ] ) )
					ch |= str[ i + j ] - '0';
				else if( islower( str[ i + j ] ) )
					ch |= str[ i + j ] - 'a' + 10;
				else // if( isupper( str[ i + j ] ) )
					ch |= str[ i + j ] - 'A' + 10;
			}
			result += ch;
			++++i;
		}
		else if( str[ i ] == '\\' && i + 1 < ( int )str.length() && IsPrinted( str[ i + 1 ] ) )
		{
			result += str[ i + 1 ];
			++i;
		}
		else
		{
			result += str[ i ];
		}
	}
	return result;
}

std::string cFormat::ValueEncode( const std::string &str )
{
	int i;
	string result;
	for( i = 0; i < ( int )str.length(); ++i )
	{
		if( str[ i ] == '%' || str[ i ] == '"' || str[ i ] == '\\' )
			result += StringPrintf( "\\%c", str[ i ] );
		else if( 32 <= ( unsigned char )str[ i ] && ( unsigned char )str[ i ] <= 255 )
			result += str[ i ];
		else
			result += StringPrintf( "%%%02x", ( int )( unsigned )( unsigned char )str[ i ] );
	}
	return result;
}

std::string cFormat::_ParseString( const char *&str )
{
	string result;
	while( IsLegal( *str ) )
	{
		if( *str == '\\' )
			result += *str++;
		result += *str++;
	}
	return Decode( result );
}

std::string cFormat::ParseQuotedString( const char *&str )
{
	string result;
	while( 32 <= ( unsigned char )*str && ( unsigned char )*str <= 255 && *str != '"' )
	{
		if( *str == '\\' )
			result += *str++;
		result += *str++;
	}
	return Decode( result );
}

cFormat cFormat::ParseValue( const char *&str )
{
	cFormat format;
	while( isspace( ( int )( unsigned )( unsigned char )*str ) )
		++str;
	string value;
	if( *str == '"' )
	{
		++str;
		value = ParseQuotedString( str );
		if( *str != '"' )
			return cFormat();
		++str;
	}
	else
		value = _ParseString( str );
	while( isspace( ( int )( unsigned )( unsigned char )*str ) )
		++str;
	if( !format.CreateValue( value ) )
		return cFormat();
	return format;
}

cFormat cFormat::ParseList( const char *&str )
{
	cFormat format;
	if( !format.CreateList() )
		return cFormat();
	while( true )
	{
		while( isspace( ( int )( unsigned )( unsigned char )*str ) )
			++str;
		if( *str == '\0' || *str == '}' )
			return format;
		string name = _ParseString( str );
		if( name.length() == 0 )
			return cFormat();
		if( !format.Insert( name ) )
			return cFormat();
		while( isspace( ( int )( unsigned )( unsigned char )*str ) )
			++str;
		if( *str == '=' )
		{
			++str;
			cFormat value = ParseValue( str );
			if( value.IsNull() )
				return cFormat();
			if( *str != ';' )
				return cFormat();
			++str;
			format[ name ] = value;
		}
		else if( *str == '{' )
		{
			++str;
			cFormat list = ParseList( str );
			if( list.IsNull() )
				return cFormat();
			if( *str != '}' )
				return cFormat();
			++str;
			if( *str == ';' )
				++str;
			format[ name ] = list;
		}
		else if( *str == '[' )
		{
			++str;
			cFormat array = ParseArray( str );
			if( array.IsNull() )
				return cFormat();
			if( *str != ']' )
				return cFormat();
			++str;
			if( *str == ';' )
				++str;
			format[ name ] = array;
		}
		else if( *str == ';' )
		{
			++str;
		}
		else
			return cFormat();
	}
}

cFormat cFormat::ParseArray( const char *&str )
{
	cFormat format;
	if( !format.CreateArray() )
		return cFormat();
	while( true )
	{
		while( isspace( ( int )( unsigned )( unsigned char )*str ) )
			++str;
		if( *str == '\0' || *str == ']' )
			return format;
		cFormat &element = format.PushBack();
		if( *str == '{' )
		{
			++str;
			cFormat list = ParseList( str );
			if( list.IsNull() )
				return cFormat();
			if( *str != '}' )
				return cFormat();
			++str;
			//if( *str != ',' )
			//	return cFormat();
			//++str;
			element = list;
		}
		else if( *str == '[' )
		{
			++str;
			cFormat array = ParseArray( str );
			if( array.IsNull() )
				return cFormat();
			if( *str != ']' )
				return cFormat();
			++str;
			//if( *str != ',' )
			//	return cFormat();
			//++str;
			element = array;
		}
		else if( *str == ',' )
		{
			//++str;
		}
		else 
		{
			cFormat value = ParseValue( str );
			if( value.IsNull() )
				return cFormat();
			//if( *str == ']' )
			//	;
			//else if( *str == ',' )
			//	++str;
			//else
			//	return cFormat();
			element = value;
		}
		while( isspace( ( int )( unsigned )( unsigned char )*str ) )
			++str;
		if( *str == ']' )
			;
		else if( *str == ',' )
			++str;
		else
			return cFormat();
	}
}

bool cFormat::CreateValue( const std::string &value )
{
	cSubValue *sub_value;
	try
	{
		sub_value = new cSubValue( value );
	}
	catch( ... )
	{
		return false;
	}
	SwitchToSubObject( sub_value );
	return true;
}

bool cFormat::CreateList()
{
	cSubList *sub_list;
	try
	{
		sub_list = new cSubList();
	}
	catch( ... )
	{
		return false;
	}
	SwitchToSubObject( sub_list );
	return true;
}

bool cFormat::CreateArray()
{
	cSubArray *sub_array;
	try
	{
		sub_array = new cSubArray();
	}
	catch( ... )
	{
		return false;
	}
	SwitchToSubObject( sub_array );
	return true;
}

bool cFormat::CreateFromString( const std::string &str, int *line, int *character )
{
	const char *str_begin = str.c_str(), *str_iterator = str_begin;
	cFormat format = ParseList( str_iterator );
	if( *str_iterator != '\0' ||
		 format.IsNull() )
	{
		if( line != NULL )
			*line = 0;
		if( character != NULL )
			*character = 0;
		for( ; str_begin != str_iterator; ++str_begin )
		{
			if( *str_begin == '\n' )
			{
				if( line != NULL )
					++*line;
				if( character != NULL )
					*character = 0;
			}
			else
			{
				if( character != NULL )
					++*character;
			}
		}
		return false;
	}
	*this = format;
	return true;
}

bool cFormat::CreateFromFile( const char file_name[], int *line, int *character )
{
	cStdFile file;
	if( !file.Open( file_name, "rt" ) )
		return false;
	string data;
	int c;
	while( ( c = file.GetChar() ) != EOF )
		data.push_back( ( char )c );
	return CreateFromString( data, line, character );
}

// for cSubList

cFormat &cFormat::operator []( const std::string &variable ) 
{
	if( !IsList() )
		if( !CreateList() )
			throw 0;
	return ( ( cSubList * )GetSubObject() )->GetMember( variable );
}

const cFormat cFormat::operator []( const std::string &variable ) const
{
	if( !IsList() )
		return cFormat();
	return ( ( const cSubList * )GetSubObject() )->GetMember( variable );
}

bool cFormat::Insert( const std::string &name )
{
	_ASSERT( IsList() );
	return ( ( cSubList * )GetSubObject() )->Insert( name );
}

void cFormat::Delete( const std::string &name )
{
	_ASSERT( IsList() );
	( ( cSubList * )GetSubObject() )->Delete( name );
}

cFormat::iterator cFormat::Begin()
{
	_ASSERT( IsList() );
	return ( ( cSubList * )GetSubObject() )->Begin();
}

cFormat::const_iterator cFormat::Begin() const
{
	_ASSERT( IsList() );
	return ( ( const cSubList * )GetSubObject() )->Begin();
}

cFormat::iterator cFormat::End()
{
	_ASSERT( IsList() );
	return ( ( cSubList * )GetSubObject() )->End();
}

cFormat::const_iterator cFormat::End() const
{
	_ASSERT( IsList() );
	return ( ( const cSubList * )GetSubObject() )->End();
}

cFormat::iterator cFormat::Find( const std::string &str )
{
	_ASSERT( IsList() );
	return ( ( cSubList * )GetSubObject() )->Find( str );
}

cFormat::const_iterator cFormat::Find( const std::string &str ) const
{
	_ASSERT( IsList() );
	return ( ( const cSubList * )GetSubObject() )->Find( str );
}

// For cSubList and cSubArray

unsigned cFormat::Size() const
{
	_ASSERT( IsList() || IsArray() );
	if( IsList() )
		return ( ( const cSubList * )GetSubObject() )->Size();
	else // if( IsArray() )
		return ( ( const cSubArray * )GetSubObject() )->Size();
}

// for cSubValue 

std::string cFormat::AsString( const std::string &default_string ) const
{
	if( !IsValue() )
		return default_string;
	return ( ( cSubValue * )GetSubObject() )->GetValue();
}

cFormat::operator std::string() const
{
	return AsString();
}

bool cFormat::operator =( const std::string &value )
{
	if( !IsValue() )
		return CreateValue( value );
	( ( cSubValue * )GetSubObject() )->SetValue( value );
	return true;
}

bool cFormat::ParseString( std::string &str ) const
{
	if( !IsValue() )
		return false;
	str = ( ( cSubValue * )GetSubObject() )->GetValue();
	return true;
}

bool cFormat::ParseInteger( int &number ) const
{
	if( !IsValue() )
		return false;
	int x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%d%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseUnsigned( unsigned &number ) const
{
	if( !IsValue() )
		return false;
	unsigned x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%u%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseLong( long &number ) const
{
	if( !IsValue() )
		return false;
	long x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%ld%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseUnsignedLong( unsigned long &number ) const
{
	if( !IsValue() )
		return false;
	unsigned long x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%lu%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseShort( short &number ) const
{
	if( !IsValue() )
		return false;
	short x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%hd%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseUnsignedShort( unsigned short &number ) const
{
	if( !IsValue() )
		return false;
	unsigned short x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%hu%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseLongLong( long long &number ) const
{
	if( !IsValue() )
		return false;
	long long x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%I64d%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseUnsignedLongLong( unsigned long long &number ) const
{
	if( !IsValue() )
		return false;
	unsigned long long x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%I64u%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseFloat( float &number ) const
{
	if( !IsValue() )
		return false;
	float x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%f%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseDouble( double &number ) const
{
	if( !IsValue() )
		return false;
	double x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%lf%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

bool cFormat::ParseLongDouble( long double &number ) const
{
	if( !IsValue() )
		return false;
	long double x;
	char dummy;
	if( sscanf( ( ( cSubValue * )GetSubObject() )->GetValue().c_str(), "%Lf%c", &x, &dummy ) != 1 )
		return false;
	number = x;
	return true;
}

// for cSubArray
cFormat &cFormat::operator []( unsigned index )
{
	if( !IsArray() )
		if( !CreateArray() )
			throw 0;
	if( Size() <= index )
		( ( cSubArray * )GetSubObject() )->Resize( index + 1 );
	return ( ( cSubArray * )GetSubObject() )->GetMember( index );
}

const cFormat cFormat::operator []( unsigned index ) const
{
	if( !IsArray() )
		return cFormat();
	return ( ( const cSubArray * )GetSubObject() )->GetMember( index );
}

void cFormat::Insert( unsigned index )
{
	_ASSERT( IsArray() );
	( ( cSubArray * )GetSubObject() )->Insert( index );
}

void cFormat::Delete( unsigned index )
{
	_ASSERT( IsArray() );
	( ( cSubArray * )GetSubObject() )->Delete( index );
}

cFormat &cFormat::PushBack()
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->PushBack();
}

cFormat cFormat::PopBack()
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->PopBack();
}

void cFormat::Resize( unsigned new_size )
{
	_ASSERT( IsArray() );
	( ( cSubArray * )GetSubObject() )->Resize( new_size );
}

void cFormat::Clear()
{
	_ASSERT( IsArray() );
	( ( cSubArray * )GetSubObject() )->Clear();
}

cFormat &cFormat::Front()
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->Front();
}

const cFormat cFormat::Front() const
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->Front();
}

cFormat &cFormat::Back()
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->Back();
}

const cFormat cFormat::Back() const
{
	_ASSERT( IsArray() );
	return ( ( cSubArray * )GetSubObject() )->Back();
}

// common

bool cFormat::ToFile( const char file_name[], bool is_formatted, int depth ) const
{
	cStdFile file;
	if( !file.Open( file_name, "wt" ) )
		return false;
	string data = ToString( is_formatted, depth );
	for( int i = 0; i < ( int )data.length(); ++i )
		file.PutChar( ( int )( unsigned )( unsigned char )data[ i ] );
	return true;
}

std::string cSubList::ToString( bool is_formatted, int depth ) const
{
	std::string result;
	for( cFormat::const_iterator list_iterator = List.begin(); list_iterator != List.end(); ++list_iterator )
	{
		int i;
		if( list_iterator->second.IsValue() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += cFormat::Encode( list_iterator->first );
			if( is_formatted )
				result += " ";
			result += "=";
			if( is_formatted )
				result += " ";
			result += list_iterator->second.ToString( is_formatted, depth + 1 );
			result += ";";
			if( is_formatted )
				result += "\n";
		}
		else if( list_iterator->second.IsList() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += cFormat::Encode( list_iterator->first );
			if( is_formatted )
				result += "\n";
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "{";
			if( is_formatted )
				result += "\n";
			result += list_iterator->second.ToString( is_formatted, depth + 1 );
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "}";
			if( is_formatted )
				result += "\n";
		}
		else if( list_iterator->second.IsArray() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += cFormat::Encode( list_iterator->first );
			if( is_formatted )
				result += "\n";
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "[";
			if( is_formatted )
				result += "\n";
			result += list_iterator->second.ToString( is_formatted, depth + 1 );
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "]";
			if( is_formatted )
				result += "\n";
		}
		else // if( list_iterator->second.IsNull() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += cFormat::Encode( list_iterator->first );
			result += ";";
			if( is_formatted )
				result += "\n";
		}
	}
	return result;
}

std::string cSubArray::ToString( bool is_formatted, int depth ) const
{
	std::string result;
	for( cFormat::tArray::const_iterator array_iterator = Array.begin(); array_iterator != Array.end(); ++array_iterator )
	{
		int i;
		if( array_iterator->IsValue() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += array_iterator->ToString( is_formatted, depth + 1 );
			result += ",";
			if( is_formatted )
				result += "\n";
		}
		else if( array_iterator->IsList() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "{";
			if( is_formatted )
				result += "\n";
			result += array_iterator->ToString( is_formatted, depth + 1 );
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "}";
			if( is_formatted )
				result += "\n";
		}
		else if( array_iterator->IsArray() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "[";
			if( is_formatted )
				result += "\n";
			result += array_iterator->ToString( is_formatted, depth + 1 );
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += "]";
			if( is_formatted )
				result += "\n";
		}
		else //if( array_iterator->IsNull() )
		{
			if( is_formatted )
			{
				for( i = 0; i < depth; ++i )
					result += '\t';
			}
			result += ",";
			if( is_formatted )
				result += "\n";
		}
	}
	return result;
}
